#ifndef _PLAYER_
#define _PLAYER_

#include "entity.h"

DelayedFunction RestartDelay;

#ifndef JUMP_ACELERATION
	#define JUMP_ACELERATION 6
#endif

#ifndef KICK_DURATION
	#define KICK_DURATION 0.6f
#endif

#define SEQUENCE_STAND			0
#define SEQUENCE_JUMP			1
#define SEQUENCE_KICK			2
#define SEQUENCE_KICK_CLOCKWISE 3
#define SEQUENCE_FALL			4

struct PlayerKeys
{
	PlayerKeys()
	{
		jump = 0x0;
		left = 0x0;
		right = 0x0;
		down = 0x0;
	}
	PlayerKeys(DWORD j,DWORD l,DWORD r,DWORD d)
	{
		jump = j;
		left = l;
		right = r;
		down = d;
	}
	DWORD jump;
	DWORD left;
	DWORD right;
	DWORD down;
};
class Player : public Entity 
{
private:
	SpriteSequence _spsq;
	PhysicsObject _phys;
	PlayerKeys _pk;
	int _seq;
	float32 _seqd;
	bool _jump;
	bool _keyboardcontrolled;
public:
	Player()
	{	
		Visible = Enabled = true;
	}
	bool Init()
	{
		bool result = this->LoadData("Entities/player",true);
		_keyboardcontrolled = true;
		return result;
	}
	void SetKeyboardData(PlayerKeys pk)
	{
		_pk = pk;
	}
	PlayerKeys GetKeyboardData(){return _pk;}
	void InputKeys(float32 TimeDelta)
	{
		if(!_keyboardcontrolled)
			return;
		if (hge->Input_GetKeyState(_pk.jump))
			SimulateKey(0);
		/*if (hge->Input_GetKeyState(_pk.down))
			SimulateKey(1);*/
		if (hge->Input_GetKeyState(_pk.left))
			SimulateKey(2);
		if (hge->Input_GetKeyState(_pk.right))
			SimulateKey(3);		
	}
	void SetKeyboardControlled(bool kbc){_keyboardcontrolled = kbc;}
	void SimulateKey(DWORD Key)
	{
		if (Key == 0)
			Jump();
		else if (hge->Input_GetKeyState(_pk.down))
		{
			if(Key == 2)
				Kick(false);
			else if(Key = 3)
				Kick(true);
		}
		if (Key == 2)
		{
			if (_seq != SEQUENCE_KICK && _seq != SEQUENCE_KICK_CLOCKWISE)
			{
				GetPhysics()->WakeUp();
				GetPhysics()->SetLinearV(b2Vec2(-3,GetPhysics()->GetLinearV().y));
			}
		}
		else if (Key == 3)
		{
			if (_seq != SEQUENCE_KICK && _seq != SEQUENCE_KICK_CLOCKWISE)
			{
				GetPhysics()->WakeUp();
				GetPhysics()->SetLinearV(b2Vec2(3,GetPhysics()->GetLinearV().y));
			}
		}
	}
	void Jump()
	{
		if (!_jump)
			return;
		GetPhysics()->ApplyAceleration(b2Vec2(0,JUMP_ACELERATION));
		_seq = SEQUENCE_JUMP;
		CanJump(false);
	}
	void CanJump(bool b)
	{
		_jump = b;
	}
	void Kick(bool ClockWise)
	{
		if(GetPhysics()->GetLinearV().y > 0 && !_jump && _seq == SEQUENCE_JUMP)
		{
			SetSequence(_seq = (ClockWise) ? SEQUENCE_KICK_CLOCKWISE : SEQUENCE_KICK , KICK_DURATION);
		}
	}
	void Stand()
	{
		CanJump(true);
		SetSequence(SEQUENCE_STAND);
	}
	void Draw(float32 dt)
	{
		if (!Enabled)
		{
			GetPhysics()->Freeze();
			return;
		}
		if (_seq == SEQUENCE_KICK_CLOCKWISE || _seq == SEQUENCE_KICK)
		{
			_seqd -= dt*speed;
			GetPhysics()->SetAngularV((_seq == SEQUENCE_KICK) ? 2*PI/KICK_DURATION : -2*PI/KICK_DURATION);
			if(_seqd <= 0)
			{
				_seqd = 0;
				_seq = SEQUENCE_FALL;
				GetPhysics()->SetAngularV(0);
				SetAngle(0);
			}
		}
		if(_seq == SEQUENCE_STAND && GetPhysics()->GetAngularV() != 0)
		{
				GetPhysics()->SetAngularV(0);
				SetAngle(0);
		}
		GetSprite()->SetAngle(-GetPhysics()->GetAngle());
		GetSprite()->Visible = Visible;
		GetSprite()->SetPosition(GetPhysics()->GetPosition());
		GetSprite()->Draw();
	}
	void SetSequence(int seq, float32 seqd = 0)
	{
		_seqd = seqd;
		_seq = seq;
	}
	int GetSequence(){return _seq;}
};

class PlayerContact : public b2ContactListener
{
public:
	PlayerContact()
	{
	}
	void Add(const b2ContactPoint* point)
    {
		// Player contact with floor for jump.
		Player * p1 = (Player*)Player1Pointer;
		Player * p2 = (Player*)Player2Pointer;
		if (( point->shape1->GetBody() == p1->GetPhysics()->GetBody() ) || ( point->shape2->GetBody() == p1->GetPhysics()->GetBody() ))
		{
			if(( point->shape1->GetBody() == ground[0].GetBody() ) || ( point->shape2->GetBody() == ground[0].GetBody() ))
			{
				p1->Stand();
			}
			return;
		}
		if (( point->shape1->GetBody() == p2->GetPhysics()->GetBody() ) || ( point->shape2->GetBody() == p2->GetPhysics()->GetBody() ))
		{
			if(( point->shape1->GetBody() == ground[0].GetBody() ) || ( point->shape2->GetBody() == ground[0].GetBody() ))
			{
				p2->Stand();
			}
			return;
		}
		// Ball contact with ground;
		Entity * BALL = (Entity*)BallPointer;
		Entity * WEB = (Entity*)WebPointer;
		if ((  point->shape1->GetBody() == BALL->GetPhysics()->GetBody()
			|| point->shape2->GetBody() == BALL->GetPhysics()->GetBody())
			&&(point->shape1->GetBody() == ground[0].GetBody()
			|| point->shape2->GetBody() == ground[0].GetBody()))
			if (BALL->GetPosition().x > WEB->GetPosition().x)
			{
				bool * winer = new bool(true);
				RestartDelay.Start(RESTART_DELAY,NULL,&RestartGame,winer);
			}
			else
			{
				bool * winer = new bool(false);
				RestartDelay.Start(RESTART_DELAY,NULL,&RestartGame,winer);
			}
		// Ball contact with delimiter
		if ((point->shape1->GetBody() == BALL->GetPhysics()->GetBody()
			|| point->shape2->GetBody() == BALL->GetPhysics()->GetBody())
			&&(point->shape1->GetBody() == playerlimits.GetBody()
			|| point->shape2->GetBody() ==  playerlimits.GetBody()))
			BALL->GetPhysics()->SetLinearV(- BALL->GetPhysics()->GetLinearV());
    }
	void Persist(const b2ContactPoint* point)
    {
        // handle persist point
    }
	void Remove(const b2ContactPoint* point)
    {
    }
    void Result(const b2ContactResult* point)
    {
        // handle results
    }

} ContactListener;

void RestartGame(void * Winer)
{
	bool winer;
	memcpy(&winer,Winer,1);
	Entity * ball = (Entity*)BallPointer;
	Player * Player1 = (Player*)Player1Pointer;
	Player * Player2 = (Player*)Player2Pointer;
	// Position of player 1:
	Player1->SetPosition(b2Vec2(100,64),true);
	Player1->SetAngle(0);
	// Place the ball on player 1.
	ball->SetPosition(b2Vec2(100,500),true);
	if (GameMode == MODE_HUMANVSHUMAN)
	{
		if (winer)
		{
			Player1Score++;
		}
		else
		{	
			// If winner was player 2, set the ball on player 2.
			ball->SetPosition(b2Vec2(GAME_WIDTH - 100,500),true);
			Player2Score++;
		}
		// Set player 2 position (only when human vs human.
		Player2->SetPosition(b2Vec2(GAME_WIDTH - 100,64),true);
		Player2->SetAngle(0);
	}
}
#endif
